/*
 * Copyright (c) 2010-2011, Petr Panteleyev <petr@panteleyev.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.flickr;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.ExecutionException;

import javax.swing.AbstractAction;
import javax.swing.JMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel;
import javax.swing.text.html.HTML;

import org.panteleyev.blogclient.BloggerEditorFrame;
import org.panteleyev.blogclient.Icon;
import org.panteleyev.blogclient.MainClass;
import org.panteleyev.blogclient.Options;
import org.panteleyev.flickrapi.FlickrAccount;
import org.panteleyev.flickrapi.FlickrPhotoSet;
import org.panteleyev.flickrapi.PhotoSize;
import org.panteleyev.flickrapi.PhotoSizeType;
import org.panteleyev.flickrapi.SizeTable;
import org.panteleyev.photo.Thumbnail;
import org.panteleyev.swing.BaseTable;
import org.panteleyev.swing.ClipboardUtil;
import org.panteleyev.swing.MenuProvider;
import org.panteleyev.swing.OSUtil;
import org.panteleyev.swing.SwingFactory;
import org.panteleyev.swing.TableUtil;
import org.panteleyev.swing.ToolBarProvider;
import org.panteleyev.utilities.Updatable;

import com.google.common.collect.ImmutableList;
import javax.swing.Action;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import org.panteleyev.swing.BasePanel;

final class PhotoSetListPanel extends BasePanel<PhotoSetListFrame> implements Updatable, MenuProvider, ToolBarProvider {
    private final String[] headerNames = {
        L10N.PHOTO_SET_LIST_TABLE_TITLE.s(),
        L10N.PHOTO_SET_LIST_TABLE_PHOTOS.s(),
    };

    private FLUserProfile       profile;
    private JTable              table;
    private AbstractTableModel  tableModel;
    private JScrollPane         scroller;
    private JMenu               menu;
    private ArrayList<AbstractAction> toolbarActions;

    private FlickrAccount       account;
    private ImmutableList<FlickrPhotoSet> photoSets;

    private AbstractAction openSetWebAction;
    private AbstractAction publishSetAction;

    /** Creates new form PhotoSetListFrame */
    public PhotoSetListPanel(PhotoSetListFrame frame, FLUserProfile profile) {
        super(frame);
        this.profile = profile;

        account = profile.getAccount();
        photoSets = profile.restorePhotoSets();

        createActions();
        initComponents();
        createTableModel();

        if (photoSets.size() == 0) {
            update();
        }
    }

    private void initComponents() {
        setLayout(new BorderLayout());

        table = new BaseTable();
        scroller = new JScrollPane(table);
        add(scroller, BorderLayout.CENTER);

        scroller.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent evt) {
                TableUtil.adjustTableColumnSizes(table, 0, scroller);
            }
        });

        table.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent evt) {
                if (evt.getClickCount() == 2) {
                   onTableDoubleClick();
                }
            }
        });

        scroller.setComponentPopupMenu(createMenu().getPopupMenu());

        Options.getBounds(this);
    }

    private FLUserProfile getProfile() {
        return profile;
    }

    public FlickrAccount getAccount() {
        return account;
    }

    private void createActions() {
        publishSetAction = new AbstractAction("Publish...", Icon.EMPTY.i()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                publishPhotoSet();
            }
        };

        openSetWebAction = new AbstractAction("Open Set", Icon.EARTH.i()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                openPhotoSetWeb();
            }
        };
        openSetWebAction.putValue(Action.LARGE_ICON_KEY, Icon.EARTH.I());

        toolbarActions = new ArrayList<>();
        toolbarActions.add(openSetWebAction);
    }

    @Override
    public JMenu createMenu() {
        return SwingFactory.createMenu("Set",
            openSetWebAction,
            null,
            publishSetAction);
    }

    @Override
    public JMenu getMenu() {
        return (menu == null) ? menu = createMenu() : menu;
    }

    @Override
    public Collection<AbstractAction> getToolBarActions() {
        return toolbarActions;
    }

    private void createTableModel() {
        tableModel = new AbstractTableModel() {
            @Override
            public int getColumnCount() { return headerNames.length; }
            @Override
            public int getRowCount() { return photoSets.size(); }
            @Override
            public Object getValueAt(int row, int col) {
                FlickrPhotoSet photoSet = photoSets.get(row);

                switch (col) {
                    case 0:
                        return photoSet.getTitle();

                    case 1:
                        return photoSet.getPhotoCount();

                    default:
                        return null;
                }
            }
            @Override
            public Class<?> getColumnClass(int columnIndex) {
                switch (columnIndex) {
                    case 0:
                        return String.class;
                    case 1:
                        return Integer.class;
                    default:
                        return String.class;
                }
            }
            @Override
            public String getColumnName(int column) { return headerNames[column]; }
        };

        table.setModel(tableModel);
        table.setRowSorter(new TableRowSorter<TableModel>(tableModel));
    }

    private void openPhotoSetWeb() {
        FlickrPhotoSet set = getSelectedPhotoSet();
        if (set != null) {
            String url = account.getPhotoSetUrl(set);
            OSUtil.runBrowser(url);
        }
    }

    private void publishPhotoSet() {
        ImmutableList<FlickrPhotoSet> selection = getSelectedPhotoSets();
        if (selection.size() == 0) {
            return;
        }

        PublishSetDialog dial = new PublishSetDialog(null, getProfile(), selection);
        if (dial.showDialog()) {
            try {
                getFrame().startLongOperation();

                StringBuilder b = new StringBuilder();

                if (dial.isUseTable()) {
                    b.append("<table");
                    if (dial.getTableBorder()) {
                        b.append(" border='1'");
                    }
                    b.append(">");
                }

                int nCols = Math.min(dial.getColumnCount(), selection.size());
                int nRows = selection.size() / nCols + (int)Math.signum(selection.size() % nCols);

                int index = 0;

                PhotoSizeType sizeType = dial.getThumbnailSize();

                for (int row = 0; row < nRows; row++) {
                    if (index >= selection.size()) {
                        break;
                    }

                    if (dial.isUseTable()) {
                        b.append("<tr valign='top' align='center'>");
                    }

                    for (int col = 0; col < nCols; col++) {
                        if (index >= selection.size()) {
                            break;
                        }

                        final Thumbnail<FlickrPhotoSet> thumbnail = dial.getThumbnail(index++);
                        final FlickrPhotoSet set = thumbnail.getPayload();
                        final String setTitle = thumbnail.getTitle();

                        if (dial.isUseTable()) {
                            b.append("<td>");
                        }
                        b.append("<a href='");
                        b.append(account.getPhotoSetUrl(set));
                        b.append("'>");

                        final String primary = set.getPrimary();

                        final SizeTable sizes = getProfile().getPhotoSizes(primary);
                        if (sizes != null) {
                            PhotoSize size = sizes.get(sizeType.value());
                            String thumbUrl = size.getSource();

                            b.append("<img src='");
                            b.append(thumbUrl);
                            b.append("' border='");

                            if (dial.getImageBorder()) {
                                b.append("1'");
                            } else {
                                b.append("0'");
                            }

                            b.append(" width='");
                            b.append(size.getWidth().toString());
                            b.append("' height='");
                            b.append(size.getHeight().toString());
                            b.append("'>");
                        }
                        b.append("</a>");

                        if (dial.isUseTable()) {
                            if (dial.isAddTitle() && setTitle != null && !setTitle.isEmpty()) {
                                b.append("<br>");
                                b.append(setTitle);
                            }
                            b.append("</td>");
                        } else {
                            b.append("\n");
                        }
                    }

                    if (dial.isUseTable()) {
                        b.append("</tr>");
                    }
                }

                if (dial.isUseTable()) {
                    b.append("</table>");
                }

                BloggerEditorFrame<?> destWnd = dial.getDestinationWindow();
                if (destWnd == null) {
                    ClipboardUtil.put(b.toString());
                } else {
                    destWnd.getEditor().insertHTML(b.toString(), HTML.Tag.TABLE);
                }
            } catch (Exception ex) {
                MainClass.handleException(getFrame(), ex);
            } finally {
                getFrame().finishLongOperation();
            }
        }
    }

    private FlickrPhotoSet getSelectedPhotoSet() {
        int index = table.getSelectedRow();
        if (index != -1) {
            return photoSets.get(table.convertRowIndexToModel(index));
        } else {
            return null;
        }
    }

    private ImmutableList<FlickrPhotoSet> getSelectedPhotoSets() {
        final int[] indexes = table.getSelectedRows();
        final ArrayList<FlickrPhotoSet> selection = new ArrayList<>(indexes.length);

        for (int ind : indexes) {
            selection.add(photoSets.get(table.convertRowIndexToModel(ind)));
        }

        return ImmutableList.copyOf(selection);
    }

    private void onTableDoubleClick() {
        FlickrPhotoSet photoSet = getSelectedPhotoSet();
        if (photoSet != null) {
            getFrame().onOpenPhotoset(photoSet);
        }
    }

    /*
    @Override
    protected boolean canClose() {
        int count = WindowManager.getFramesCount(getProfile(), this.getClass());
        if (count == 1) {
            if (MainClass.showConfirmDialog(this)) {
                // Close all profile related frames
                WindowManager.sendApplicationEvent(getProfile(), new CloseFrameEvent(this));
                getProfile().setConnected(false);
                return true;
            }
            return false;
        }

        return super.canClose();
    }
    */

    @Override
    public void update() {
        getFrame().startLongOperation();
        new SwingWorker<Object,Object>() {
            @Override
            protected Object doInBackground() throws Exception {
                photoSets = getAccount().loadPhotoSetList();
                getProfile().storePhotoSets(photoSets);
                return null;
            }

            @Override
            protected void done() {
                try {
                    get();
                } catch (InterruptedException | ExecutionException ex) {
                    MainClass.handleException(getFrame(), ex);
                } finally {
                    tableModel.fireTableDataChanged();
                    TableUtil.adjustTableColumnSizes(table, 0, scroller);
                    getFrame().finishLongOperation();
                }
            }
        }.execute();
    }

}
